/***********************************************************************************
 * 文件名： polsys.c
 * 版本： 4
 * 说明：
 * 		polsys 核心实现。 
 *      【polsys】
 *      使用软定时器原理的“多任务”系统, 轮询调用入口函数，非RTOS。支持在任意地方创建任务。
 *      【polsys_lite】
 *      通过在用户端提供宏定义配置的方式让用户添加几个固定间隔时间需要执行的任务函数，然后在
 *  Polsys_Loop 定时调用他们。 这样的机制可以在不支持函数指针的“低端RISC 8位单片机”中帮助代
 *  码去耦，让软件更具模块化和面向对象。
 *      【通用说明】
 *      polsys_port.c 文件中已经编写好移植示例，详见该文件内的注释说明。
 * 修改记录：
 * 	2021/04/28: 初版。 刘言。
 *  2021/05/19：合并polsys_lite。刘言。
 *  2021/11/15: 
 * 		定义 _POLSYS_DEBUG 后可以查看CPU使用率、是否过载等调试信息，每个TICK周期更新，过载
 * 	表示在一个TICK周期内不能执行完所有任务，一旦置位复位才能清除。
 * 		优化任务调度，现在只在有任务状态被标记为 TS_READY 后才会去扫描任务列表寻找并执行需要
 * 	执行的任务，其它时间都循环在执行 SYS_IDLE() 。刘言。
 * 	2022/11/28:
 * 		修复CPU使用率可能错误的BUG；
 * 		更改 IDLE 任务名为 Polsys_EventIdle；
 * 		新增优先级功能，间隔时间参数的高3位表示优先级，目前仅支持7优先级（实时任务，在中断中执行）。
 * 		依据 POLSYS_TICK_CYCLE 来设置任务运行间隔时间，API统一ms单位。
 * 		新增 Polsys_SetTaskReady 函数。
***********************************************************************************/
#include "polsys.h"

#ifdef POLSYS_LITE

bit _Polsys_mTicked = false;

#ifdef _POLSYS_TASK_GROUP_1000TICK
u8 _Polsys_TickCount = 0;
u8 _Polsys_10TickCount = 0;
u8 _Polsys_100TickCount = 0;
#elif (defined _POLSYS_TASK_GROUP_100TICK)
u8 _Polsys_TickCount = 0;
u8 _Polsys_10TickCount = 0;
#elif (defined _POLSYS_TASK_GROUP_10TICK)
u8 _Polsys_TickCount = 0;
#endif

#else

#ifdef POLSYS_USE_PARAM
#define __PARAM cTask->Param
#else
#define __PARAM  
#endif

//自定义数据类型

typedef struct _sys_info
{
	polsys_task_t *Tasks[POLSYS_MAX_TASKS]; //所有的任务列表
#ifdef _POLSYS_DEBUG
	bool Busy;					// false:空闲
	u8 NowRunTaskNo;			    // 现在正在运行的任务号
	u8 CpuUsageAvg;					// 500ms内CPU平均使用率
	u8 CpuUsageMax;					// 3秒内单TICK周期最大的CPU使用率
	u8 CpuUsageLastTick;			// 一个TICK周期内CPU总体使用率
	u16 CpuUsageAdd;				// 500ms内CPU累积使用率，用于计算平均使用率
	int IdleClocks;					// 一个TICK周期内系统空闲的定时器时钟周期数（空闲时间），用于计算CPU使用率。
	u32 Overload;					// 过载计数，在一个TICK周期内不能执行完所有任务，复位才能清除。
	u32 TickCnt;					// Tick计数，用于开机计时，不会清零
#endif
	u8 TotalTask;				    // 已添加的任务数量
	bool Need;						// 标记：有任务需要执行。用于判断是否需要重新扫描一遍需要执行的任务并执行。一般会立即清除并执行任务，除非某个任务特别耗时，因此长时间标记说明有任务特别耗时，系统因为过于繁忙而不能确保任务及时响应。
} sys_info_t;

#ifdef _POLSYS_DEBUG
#define SYS_INFO_DEFAULT	\
{	\
	{NULL},	\
	false,	\
	255,	\
	0,	\
	0,	\
	0,	\
	0,	\
	0,	\
	0,	\
	0,	\
	0,	\
	false,	\
}
#else
#define SYS_INFO_DEFAULT	\
{	\
	{NULL},	\
	0,	\
	false,	\
}
#endif

// 外部函数声明

#ifdef _POLSYS_USE_IDLE_CB
void Polsys_EventIdle();
#endif

// 内部属性定义

idata volatile sys_info_t SysInfo = SYS_INFO_DEFAULT;

#ifdef _POLSYS_DEBUG
static volatile bool mTicked = false;	// 标记：Tick中断已经发生
static volatile int mTaskStartClocks = 0;		// 单个任务开始执行时的定时器值(时钟数)
static volatile int mIdleStartClocks = 0;		// 进入空闲时的定时器值（时钟数）
#endif


void Polsys_Tick() //定时器中断调用 系统节拍 间隔 POLSYS_TICK_CYCLE ms
{
	u8 i;
	polsys_task_t *cTask;

#ifdef _POLSYS_DEBUG
	if(SysInfo.NowRunTaskNo < 255)	// 有任务正在执行而被打断
	{
		int now_value = _Polsys_GetTickTimerNowValue();
		if(now_value < mTaskStartClocks)now_value = _Polsys_GetTickTimerCycleValue();	// 跨周期重载了（大多数情况，但是也有例外，比如重载后没有及时响应中断，而先记录了mTaskStartClocks，之后才响应了中断）
		SysInfo.Tasks[SysInfo.NowRunTaskNo]->RunClocks += now_value - mTaskStartClocks;	// 结算 RunClocks
		// mTaskStartClocks = (int)_Polsys_GetTickTimerNowValue(); 这里会把中断的执行时间算到正在执行的任务头上，不可取，放在末尾
	}
#endif
	for (i = 0; i < SysInfo.TotalTask; i++)
	{
		cTask = SysInfo.Tasks[i];
#ifdef _POLSYS_DEBUG
		// 计算每个任务上一个TICK周期的CPU使用率
		cTask->CpuUsageLastTick = cTask->RunClocks * 100 / (int)_Polsys_GetTickTimerCycleValue();
		// 获取CPU使用率最大值
		if(cTask->CpuUsageMax < cTask->CpuUsageLastTick)cTask->CpuUsageMax = cTask->CpuUsageLastTick;
		// 清零
		cTask->RunClocks = 0;
		// 累加CPU使用率
		cTask->CpuUsageAdd += cTask->CpuUsageLastTick;
#endif
		if (cTask->WaitTime < POLSYS_WAITING_FOREVER && cTask->WaitTime > 0)
			cTask->WaitTime--;
		if (cTask->WaitTime == 0)
		{
			cTask->Status = TS_READY;
			cTask->WaitTime = cTask->Interval;
			SysInfo.Need = true;
		}
#ifdef POLSYS_USE_PRIO
		if(cTask->Prio == 7)
		{
			if (cTask->Status == TS_READY)
			{
	#ifdef _POLSYS_DEBUG
				u8 backup = SysInfo.NowRunTaskNo;
				SysInfo.NowRunTaskNo = i;
				mTaskStartClocks = (int)_Polsys_GetTickTimerNowValue();
	#endif
				cTask->Status = TS_RUNNING;
				if(cTask->EntryFunc)cTask->EntryFunc(__PARAM);
				cTask->Status = TS_WAIT; // 标记为等待状态
	#ifdef _POLSYS_DEBUG
				cTask->RunClocks = (int)_Polsys_GetTickTimerNowValue() - mTaskStartClocks;
				SysInfo.NowRunTaskNo = backup;
	#endif
			}
		}
#endif
	}

#ifdef _POLSYS_DEBUG
	SysInfo.TickCnt++;
	if(SysInfo.Busy == false)	// 空闲
	{
		int now_value = (int)_Polsys_GetTickTimerNowValue();
		if(now_value < mIdleStartClocks)now_value = _Polsys_GetTickTimerCycleValue();	// 跨周期重载了（大多数情况，但是也有例外，比如重载后没有及时响应中断，而先记录了mTaskStartClocks，之后才响应了中断）
		SysInfo.IdleClocks += now_value - mIdleStartClocks;	// 结算 上一个TICK周期的 IdleClocks
		// mIdleStartClocks = (int)_Polsys_GetTickTimerNowValue(); 这里会把中断的执行时间算到IDLE时间，不可取，放在末尾
	}
	// 系统过载判断
	if(mTicked == true)	// 系统因为繁忙而没有清除上个周期的mTicked
	{
		// SysInfo.CpuUsageLastTick = 100;	// 总体CPU占用率为 100
		SysInfo.Overload++;	// 过载次数+1，本次TICK周期CPU满载，无空闲时间，任务响应已经延迟
	}
	mTicked = true;
	// 计算上一个周期的总体CPU使用率
	SysInfo.CpuUsageLastTick = 100 - SysInfo.IdleClocks * 100 / (int)_Polsys_GetTickTimerCycleValue();
	SysInfo.IdleClocks = 0;
	// 总体CPU使用率最大值
	if(SysInfo.CpuUsageMax < SysInfo.CpuUsageLastTick)SysInfo.CpuUsageMax = SysInfo.CpuUsageLastTick;
	// 每隔10秒清零CPU使用率最大值
	if(SysInfo.TickCnt % (10000 / POLSYS_TICK_CYCLE) == 0)	
	{
		SysInfo.CpuUsageMax = SysInfo.CpuUsageLastTick;
		for (i = 0; i < SysInfo.TotalTask; i++)
		{
			cTask = SysInfo.Tasks[i];
			cTask->CpuUsageMax = cTask->CpuUsageLastTick;
		}
	}
	// 累加总体CPU使用率
	SysInfo.CpuUsageAdd += SysInfo.CpuUsageLastTick;
	// 每隔500ms计算平均值
	if(SysInfo.TickCnt % (500 / POLSYS_TICK_CYCLE) == 0)	
	{
		SysInfo.CpuUsageAvg = SysInfo.CpuUsageAdd / (500 / POLSYS_TICK_CYCLE);
		SysInfo.CpuUsageAdd = 0;
		for (i = 0; i < SysInfo.TotalTask; i++)
		{
			cTask = SysInfo.Tasks[i];
			cTask->CpuUsageAvg = cTask->CpuUsageAdd / (500 / POLSYS_TICK_CYCLE);
			cTask->CpuUsageAdd = 0;
		}
	}
	if(SysInfo.Busy == false)	// 空闲
	{
		mIdleStartClocks = (int)_Polsys_GetTickTimerNowValue();
	}
	if(SysInfo.NowRunTaskNo < 255)	// 有任务正在执行而被打断
	{
		mTaskStartClocks = (int)_Polsys_GetTickTimerNowValue();	// 重新统计本周期的运行时钟数
	}
#endif
}

void Polsys_Loop() //系统循环，用于执行任务，始终循环调用
{
	u8 i;
	polsys_task_t *cTask;

	while(SysInfo.Need)	// 有任务需要执行
	{
		SysInfo.Need = false;
		for (i = 0; i < SysInfo.TotalTask; i++)
		{
			cTask = SysInfo.Tasks[i];
#ifdef POLSYS_USE_PRIO
			if(cTask->Prio < 7)	// 实时任务不在这里执行，在Tick中断中执行
#endif
			if (cTask->Status == TS_READY)
			{
	#ifdef _POLSYS_DEBUG
				Polsys_DisableInt();
				mTaskStartClocks = (int)_Polsys_GetTickTimerNowValue();
				SysInfo.NowRunTaskNo = i;
				Polsys_EnableInt();
	#endif
				cTask->Status = TS_RUNNING;
				if(cTask->EntryFunc)cTask->EntryFunc(__PARAM);
				Polsys_DisableInt();	// 确保不会出现刚判断 != TS_READY 后中断发生又设置为 TS_READY 导致任务得不到执行的状况。
				if (cTask->Interval > 0)
				{
					if(cTask->Status != TS_READY)cTask->Status = TS_WAIT; // 标记为等待状态
				}
				else						// 没有间隔时间的任务始终READY
				{
					cTask->Status = TS_READY;
					SysInfo.Need = true;
				}
				Polsys_EnableInt();
	#ifdef _POLSYS_DEBUG
				Polsys_DisableInt();
				int now_value = (int)_Polsys_GetTickTimerNowValue();
				if(now_value < mTaskStartClocks)now_value = _Polsys_GetTickTimerCycleValue();
				cTask->RunClocks += now_value - mTaskStartClocks;
				SysInfo.NowRunTaskNo = 255;	// 标记未执行任何任务
				Polsys_EnableInt();
	#endif
			}
		}
	}
	// READY的任务执行完毕了
#ifdef _POLSYS_DEBUG
	Polsys_DisableInt();
	mIdleStartClocks = (int)_Polsys_GetTickTimerNowValue();
	SysInfo.Busy = false;
	Polsys_EnableInt();
#endif
	// 空闲了，暂时没有任务需要执行了。
	while(SysInfo.Need == false)
	{
#ifdef _POLSYS_USE_IDLE_CB
	#ifdef _POLSYS_DEBUG
		mTicked = false;
	#endif
		Polsys_EventIdle();		// 阻塞后，每次TICK中断都要执行出来便于清零mTicked，无论是否Need.（如果_POLSYS_DEBUG）
#else
	#ifdef _POLSYS_DEBUG
		mTicked = false;
		while((mTicked == false) && (SysInfo.Need == false));
	#endif
#endif
	}
#ifdef _POLSYS_DEBUG
	Polsys_DisableInt();
	int now_value = (int)_Polsys_GetTickTimerNowValue();
	if(now_value < mIdleStartClocks)now_value = _Polsys_GetTickTimerCycleValue();
	SysInfo.IdleClocks += now_value - mIdleStartClocks;
	mIdleStartClocks = now_value;
	SysInfo.Busy = true;
	Polsys_EnableInt();
#endif
}

void Polsys_Init()
{
	_Polsys_ClockInit();	//系统时钟初始化
	_Polsys_TickStart();
	Polsys_EnableInt();
}

#ifdef POLSYS_USE_PARAM
bool Polsys_InitTaskP(polsys_task_t *task_ptr, polsys_task_entry_t entry, void *param, u16 interval)
#else
bool Polsys_TaskInit(polsys_task_t *task_ptr, polsys_task_entry_t entry, u16 interval)
#endif
{
	if (SysInfo.TotalTask >= POLSYS_MAX_TASKS)
		return false;

#ifdef POLSYS_USE_PRIO
	task_ptr->Prio = interval >> 13;
#endif
	interval &= 0x1FFF;
	if(interval < POLSYS_WAITING_FOREVER)
	{
		interval = (interval + (POLSYS_TICK_CYCLE - 1)) / POLSYS_TICK_CYCLE;
	}
	// task 属性
	task_ptr->EntryFunc = entry;
#ifdef POLSYS_USE_PARAM
	task_ptr->Param = param;
#endif
	task_ptr->Interval = interval;
	task_ptr->WaitTime = interval;
	task_ptr->Status = TS_WAIT;
#ifdef _POLSYS_DEBUG
	task_ptr->CpuUsageLastTick = 0;
	task_ptr->CpuUsageMax = 0;
	task_ptr->CpuUsageAvg = 0;
	task_ptr->CpuUsageAdd = 0;
	task_ptr->RunClocks = 0;
#endif

	SysInfo.Tasks[SysInfo.TotalTask] = task_ptr;
	// 添加到 task 列表

	SysInfo.TotalTask++;

	return true;
}

IN_LINE void Polsys_SetTaskReady(polsys_task_t *task_ptr)
{
	task_ptr->Status = TS_READY;
	SysInfo.Need = true;
	Polsys_SetThreadReady();	// 判断一下如果是 polsys 线程就不必调用
}

void Polsys_SetInterval(polsys_task_t *task_ptr, u16 interval)
{
#ifdef POLSYS_USE_PRIO
	task_ptr->Prio = interval >> 13;
#endif
	interval &= 0x1FFF;
	if(interval < POLSYS_WAITING_FOREVER)
	{
		interval = (interval + (POLSYS_TICK_CYCLE - 1)) / POLSYS_TICK_CYCLE;
	}
	Polsys_DisableInt();
	task_ptr->Interval = interval;
	task_ptr->WaitTime = interval;
	task_ptr->Status = TS_WAIT;
	Polsys_EnableInt();
}

#ifdef POLSYS_USE_PARAM
/**
 * 设置任务入口
 * @param entry 任务入口函数
 * @param param 入口函数的参数
*/
void Polsys_SetEntry(polsys_task_t *task_ptr, polsys_task_entry_t entry, void *param)
{
	Polsys_DisableInt();
	task_ptr->Param = param;
	task_ptr->EntryFunc = entry;
	Polsys_EnableInt();
}
#else
/**
 * 设置任务入口
 * @param entry 任务入口函数
*/
void Polsys_SetEntry(polsys_task_t *task_ptr, polsys_task_entry_t entry)
{
	Polsys_EnableInt();
	task_ptr->EntryFunc = entry;
	Polsys_EnableInt();
}
#endif

#endif

